use core::fmt;
use std::fmt::{Debug, Display, Formatter};
use std::hash::Hash;
use std::ops::{Add, Deref, Sub};

use num::Num;

use crate::gen_title;

pub fn ch2_20_1() {
    gen_title("ch2_20_1", 18);

    pub trait Iterator {
        type Item;

        fn next(&mut self) -> Option<Self::Item>;
    }

    struct Counter;

    impl Iterator for Counter {
        type Item = u32;

        fn next(&mut self) -> Option<Self::Item> {
            Some(12)
        }
    }

    let mut c = Counter;
    println!("{:?}", c.next());
}

pub fn ch2_20_2() {
    gen_title("ch2_20_2", 18);

    pub trait CacheableItem: Clone + Default + fmt::Debug {
        type Address: AsRef<[u8]> + Clone + fmt::Debug + Eq + Hash;
        fn is_null(&self) -> bool;
    }

    trait Container<A, B> {
        fn contains(&self, a: A, b: B) -> bool;
    }

    fn difference<A, B, C>(container: &C) -> i32
    where
        C: Container<A, B>,
    {
        12
    }

    trait ContainerSimple: Debug {
        type A;
        type B;
        fn contains(&self, a: &Self::A, b: &Self::B) -> bool;
    }

    fn difference_simple<C: ContainerSimple>(container: &C) {
        println!("{:?}", container);
    }

    #[derive(Debug)]
    struct Example;

    impl ContainerSimple for Example {
        type A = i32;
        type B = i32;

        fn contains(&self, a: &Self::A, b: &Self::B) -> bool {
            a == b
        }
    }

    let exp = Example;
    println!("{}", exp.contains(&1, &1));
}

pub fn ch2_20_3() {
    gen_title("ch2_20_3", 18);

    #[derive(Debug, PartialEq)]
    struct Point {
        x: i32,
        y: i32,
    }

    impl Add for Point {
        type Output = Point;

        fn add(self, other: Point) -> Point {
            Point {
                x: self.x + other.x,
                y: self.y + other.y,
            }
        }
    }

    assert_eq!(
        Point { x: 1, y: 0 } + Point { x: 2, y: 3 },
        Point { x: 3, y: 3 }
    );
    println!("{:?}", Point { x: 1, y: 0 } + Point { x: 2, y: 3 });
}

pub fn ch2_20_4() {
    gen_title("ch2_20_4", 18);

    #[derive(Debug)]
    struct Millimeters(u32);
    struct Meters(u32);

    impl Add<Meters> for Millimeters {
        type Output = Millimeters;

        fn add(self, other: Meters) -> Self::Output {
            Millimeters(self.0 + (other.0 * 1000))
        }
    }

    let mill = Millimeters(10);
    let met = Meters(12);
    println!("{:?}", mill + met);
}

pub fn ch2_20_5() {
    gen_title("ch2_20_5", 18);

    trait Pilot {
        fn fly(&self);
    }

    trait Wizard {
        fn fly(&self);
    }

    struct Human;

    impl Pilot for Human {
        fn fly(&self) {
            println!("This is your captain speaking.");
        }
    }

    impl Wizard for Human {
        fn fly(&self) {
            println!("Up!");
        }
    }

    impl Human {
        fn fly(&self) {
            println!("*waving arms furiously*");
        }
    }

    let person = Human;
    person.fly();

    Pilot::fly(&person);
    Wizard::fly(&person);
}

pub fn ch2_20_6() {
    gen_title("ch2_20_6", 18);

    trait Animal {
        fn baby_name() -> String;
    }

    struct Dog;

    impl Dog {
        fn baby_name() -> String {
            String::from("Spot")
        }
    }

    impl Animal for Dog {
        fn baby_name() -> String {
            String::from("puppy")
        }
    }

    println!("{}", Dog::baby_name());
    println!("{}", <Dog as Animal>::baby_name())
}

pub fn ch2_20_7() {
    gen_title("ch2_20_7", 18);

    trait OutlinePrint: Display {
        fn outline_print(&self) {
            let output = self.to_string();
            let len = output.len();
            println!("{}", "*".repeat(len + 4));
            println!("*{}*", " ".repeat(len + 2));
            println!("* {} *", output);
            println!("*{}*", " ".repeat(len + 2));
            println!("{}", "*".repeat(len + 4));
        }
    }

    struct Point {
        x: i32,
        y: i32,
    }

    impl Display for Point {
        fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
            write!(f, "({}, {})", self.x, self.y)
        }
    }
    impl OutlinePrint for Point {}

    let point = Point { x: 1, y: 2 };

    point.outline_print();
}

pub fn ch2_20_8() {
    gen_title("ch2_20_8", 18);

    struct Wrapper(Vec<String>);

    impl Deref for Wrapper {
        type Target = Vec<String>;

        fn deref(&self) -> &Self::Target {
            &self.0
        }
    }

    impl Display for Wrapper {
        fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
            write!(f, "[{}]", self.join(", "))
        }
    }

    let w = Wrapper(vec![String::from("hello"), String::from("world")]);
    println!("{}", w);
}

pub fn ch2_20_pra_1() {
    gen_title("ch2_20_pra_1", 18);

    struct Container(i32, i32);

    trait Contains<A, B> {
        fn contains(&self, a: &A, b: &B) -> bool;

        fn first(&self) -> i32;

        fn last(&self) -> i32;
    }

    impl Contains<i32, i32> for Container {
        fn contains(&self, a: &i32, b: &i32) -> bool {
            (&self.0 == a) && (&self.1 == b)
        }

        fn first(&self) -> i32 {
            self.0
        }

        fn last(&self) -> i32 {
            self.1
        }
    }

    fn difference<A, B, C: Contains<A, B>>(container: &C) -> i32 {
        container.last() - container.first()
    }

    let number_1 = 3;
    let number_2 = 10;

    let container = Container(number_1, number_2);

    println!(
        "Does container contain {} and {}: {}",
        &number_1,
        &number_2,
        container.contains(&number_1, &number_2)
    );

    println!("First number: {}", container.first());
    println!("Last number: {}", container.last());

    println!("The difference is: {}", difference(&container));
}

pub fn ch2_20_pra_2() {
    gen_title("ch2_20_pra_2", 18);

    trait Contains {
        type A;
        type B;

        fn contains(&self, a: &Self::A, b: &Self::B) -> bool;
        fn first(&self) -> i32;
        fn last(&self) -> i32;
    }

    struct Container(i32, i32);

    impl Contains for Container {
        type A = i32;
        type B = i32;

        fn contains(&self, number_1: &Self::A, number_2: &Self::B) -> bool {
            (&self.0 == number_1) && (&self.1 == number_2)
        }

        fn first(&self) -> i32 {
            self.0
        }

        fn last(&self) -> i32 {
            self.1
        }
    }

    fn difference<C: Contains>(container: &C) -> i32 {
        container.last() - container.first()
    }

    let number_1 = 3;
    let number_2 = 10;

    let container = Container(number_1, number_2);

    println!(
        "Does container contain {} and {}: {}",
        &number_1,
        &number_2,
        container.contains(&number_1, &number_2)
    );
    println!("First number: {}", container.first());
    println!("Last number: {}", container.last());

    println!("The difference is: {}", difference(&container));
}

pub fn ch2_20_pra_3() {
    gen_title("ch2_20_pra_3", 18);

    #[derive(Debug, PartialEq)]
    struct Point<T> {
        x: T,
        y: T,
    }

    impl<T: Num> Sub for Point<T> {
        type Output = Self;

        fn sub(self, other: Self) -> Self::Output {
            Point {
                x: self.x - other.x,
                y: self.y - other.y,
            }
        }
    }

    assert_eq!(
        Point { x: 2, y: 3 } - Point { x: 1, y: 0 },
        Point { x: 1, y: 3 }
    );
    println!("{:?}", Point { x: 2, y: 3 } - Point { x: 1, y: 0 });
}

pub fn ch2_20_pra_4() {
    gen_title("ch2_20_pra_5", 18);

    trait UsernameWidget {
        fn get(&self) -> String;
    }

    trait AgeWidget {
        fn get(&self) -> u8;
    }

    struct Form {
        username: String,
        age: u8,
    }

    impl UsernameWidget for Form {
        fn get(&self) -> String {
            self.username.clone()
        }
    }

    impl AgeWidget for Form {
        fn get(&self) -> u8 {
            self.age
        }
    }

    let form = Form {
        username: "rustacean".to_string(),
        age: 28,
    };

    let username = UsernameWidget::get(&form);
    assert_eq!("rustacean".to_owned(), username);
    let age = AgeWidget::get(&form);
    assert_eq!(28, age);

    println!("{}", <Form as AgeWidget>::get(&form))
}

pub fn ch2_20_pra_5() {
    gen_title("ch2_20_pra_5", 18);

    trait Pilot {
        fn fly(&self) -> String;
    }

    trait Wizard {
        fn fly(&self) -> String;
    }

    struct Human;

    impl Pilot for Human {
        fn fly(&self) -> String {
            String::from("This is your captain speaking.")
        }
    }

    impl Wizard for Human {
        fn fly(&self) -> String {
            String::from("Up!")
        }
    }

    impl Human {
        fn fly(&self) -> String {
            String::from("*waving arms furiously*")
        }
    }

    let person = Human;

    assert_eq!("This is your captain speaking.", Pilot::fly(&person));
    assert_eq!("Up!", Wizard::fly(&person));
    assert_eq!("*waving arms furiously*", person.fly());

    println!("success");
}

pub fn ch2_20_pra_6() {
    gen_title("ch2_20_pra_6", 18);

    trait Person {
        fn name(&self) -> String;
    }

    trait Student: Person {
        fn university(&self) -> String;
    }

    trait Programmer {
        fn fav_language(&self) -> String;
    }

    trait CompSciStudent: Programmer + Student {
        fn git_username(&self) -> String;
    }

    fn comp_sci_student_greeting(student: &dyn CompSciStudent) -> String {
        format!(
            "My name is {} and I attend {}. My favorite language is {}. My Git username is {}",
            student.name(),
            student.university(),
            student.fav_language(),
            student.git_username()
        )
    }

    struct CSStudent {
        name: String,
        university: String,
        fav_language: String,
        git_username: String,
    }

    impl Programmer for CSStudent {
        fn fav_language(&self) -> String {
            self.fav_language.clone()
        }
    }
    impl Student for CSStudent {
        fn university(&self) -> String {
            self.university.clone()
        }
    }
    impl Person for CSStudent {
        fn name(&self) -> String {
            self.name.clone()
        }
    }
    impl CompSciStudent for CSStudent {
        fn git_username(&self) -> String {
            self.git_username.clone()
        }
    }

    let student = CSStudent {
        name: "Sunfei".to_string(),
        university: "XXX".to_string(),
        fav_language: "Rust".to_string(),
        git_username: "sunface".to_string(),
    };

    // 填空
    println!("{}", comp_sci_student_greeting(&student));
}

pub fn ch2_20_pra_7() {
    gen_title("ch2_20_pra_7", 18);

    struct Pretty(String);

    impl Display for Pretty {
        fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
            write!(f, "\"{}\"", self.0.clone() + ", world")
        }
    }

    let w = Pretty("hello".to_string());
    println!("w = {}", w);
}
